#pragma once

#include "njm_entity.hpp"
#include "njm_component.hpp"
#include <array>
#include <unordered_map>
#include <cassert>

namespace njm
{
    class IComponentArray
    {
    public:
        virtual ~IComponentArray() = default;
        virtual void EntityDestroyed(Entity entity) = 0;
    };

    template <typename T>
    class ComponentArray : public IComponentArray
    {
    public:
    
        void InsertData(Entity entity, T component)
        {
            assert(entityToIndex.find(entity) == entityToIndex.end() && "Component added to same entity more than once.");
            // Put new entry at end and update the maps
            size_t newIndex = entityToIndex.size();
            entityToIndex[entity] = newIndex;
            indexToEntity[newIndex] = entity;
            componentArray[newIndex] = component;
            ++size;
        }

        void RemoveData(Entity entity)
        {
            assert(entityToIndex.find(entity) != entityToIndex.end() && "Removing non-existent component.");
            // 把最后一个元素放到被删除的位置，以保持数组数据的紧凑性
            size_t indexOfRemovedEntity = entityToIndex[entity];
            size_t indexOfLastElement = size - 1;
            componentArray[indexOfRemovedEntity] = componentArray[indexOfLastElement];
            // 更新映射
            Entity entityOfLastElement = indexToEntity[indexOfLastElement];
            entityToIndex[entityOfLastElement] = indexOfRemovedEntity;
            indexToEntity[indexOfRemovedEntity] = entityOfLastElement;

            entityToIndex.erase(entity);
            indexToEntity.erase(indexOfLastElement);
            --size;
        }

        T &GetData(Entity entity)
        {
            assert(entityToIndex.find(entity) != entityToIndex.end() && "Retrieving non-existent component.");
            return componentArray[entityToIndex[entity]];
        }

        void SetData(Entity entity, T component)
        {
            assert(entityToIndex.find(entity) != entityToIndex.end() && "Setting non-existent component.");
            componentArray[entityToIndex[entity]] = component;
        }

        void EntityDestroyed(Entity entity)
        {
            if (entityToIndex.find(entity) != entityToIndex.end())
            {
                RemoveData(entity);
            }
        }

    private:
        std::array<T, maxEntities> componentArray;
        std::unordered_map<Entity, size_t> entityToIndex;
        std::unordered_map<size_t, Entity> indexToEntity;
        size_t size = 0;
    };
}
